import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Timer;


public class Intersection extends Thread{
	
	static int nextId = 0;
	
	HashMap<Road, Boolean> lights = new HashMap<>();
	HashMap<Road, Integer> priorities = new HashMap<>();
	HashMap<Road, ArrayList<Car>> cars = new HashMap<>();
	HashMap<Road, Integer> counters = new HashMap<>();
	HashMap<Road, Intersection> intersections = new HashMap<>();

	int id;
	
	Road light = Road.NORTH;
	
	ArrayList<Integer> waitingTime;
	
	int counter = 0;
	int timesToCheck = 10;
	
	Timer timer = new Timer();
	
	volatile boolean Running = true;
	
	boolean isSmart;
	
	public Intersection(ArrayList<Integer> waitingTime, boolean smart) {
		
		isSmart = smart;
		
		this.waitingTime = waitingTime;
		this.id = nextId++;
		
		this.cars.put(Road.NORTH, new ArrayList<Car>());
		this.cars.put(Road.WEST, new ArrayList<Car>());
		this.cars.put(Road.EAST, new ArrayList<Car>());
		this.cars.put(Road.SOUTH, new ArrayList<Car>());
		
		this.priorities.put(Road.NORTH, 0 );
		this.priorities.put(Road.WEST, 0 );
		this.priorities.put(Road.EAST, 0 );
		this.priorities.put(Road.SOUTH, 0 );
		
		this.counters.put(Road.NORTH, 0 );
		this.counters.put(Road.WEST, 0 );
		this.counters.put(Road.EAST, 0 );
		this.counters.put(Road.SOUTH, 0 );
		
	}
	
	public void setNeighbours(HashMap<Road, Intersection> intersections) {
		this.intersections = intersections;
	}
	
	public void fillArrays(ArrayList<Car> north, ArrayList<Car> east, ArrayList<Car> west, ArrayList<Car> south) {
		this.cars.put(Road.NORTH, north);
		this.cars.put(Road.WEST, west);
		this.cars.put(Road.EAST, east);
		this.cars.put(Road.SOUTH, south);
	}
	
	
	public void changeLight() {
		if(isSmart) {
			changeLightSmart();
		} else {
			changeLightDumb();
		}
	}
	
	public Road getMaxPriority() {
		int max = cars.get(Road.NORTH).size();
		
		Road direction = Road.NORTH;
		
		if(max < priorities.get(Road.SOUTH) ) {
			max = priorities.get(Road.SOUTH);
			direction = Road.SOUTH;
		}
		if(max < priorities.get(Road.EAST) ) {
			max = priorities.get(Road.EAST);
			direction = Road.EAST;
		}
		if(max < priorities.get(Road.WEST) ) {
			max = priorities.get(Road.WEST);
			direction = Road.WEST;
		}
		
		return direction;
	}
	
	public Road getLongest() {
		int max = cars.get(Road.NORTH).size();
		
		Road direction = Road.NORTH;
		
		if(max < cars.get(Road.SOUTH).size() || 
				( max == cars.get(Road.SOUTH).size() && counters.get(Road.SOUTH) > counters.get(direction) )) {
			max = cars.get(Road.SOUTH).size();
			direction = Road.SOUTH;
		}
		if(max < cars.get(Road.EAST).size() || 
				( max == cars.get(Road.EAST).size() && counters.get(Road.EAST) > counters.get(direction) )) {
			max = cars.get(Road.EAST).size();
			direction = Road.EAST;
		}
		if(max < cars.get(Road.WEST).size() || 
				( max == cars.get(Road.WEST).size() && counters.get(Road.WEST) > counters.get(direction) )) {
			max = cars.get(Road.WEST).size();
			direction = Road.WEST;
		}
		
		return direction;
		
	}
	
	public void setPriorities() {
		Road longest = getLongest();
		priorities.put(longest, 5);
		if(longest != Road.EAST && cars.get(Road.EAST).size() > 0)
			priorities.put(Road.EAST, priorities.get(Road.EAST) + 1 );
		if(longest != Road.NORTH && cars.get(Road.NORTH).size() > 0)
			priorities.put(Road.NORTH, priorities.get(Road.NORTH) + 1 );
		if(longest != Road.WEST && cars.get(Road.WEST).size() > 0)
			priorities.put(Road.WEST, priorities.get(Road.WEST) + 1 );
		if(longest != Road.SOUTH && cars.get(Road.SOUTH).size() > 0)
			priorities.put(Road.SOUTH, priorities.get(Road.SOUTH) + 1 );
	}
	
	public void changeLightSmart() {
		
		setPriorities();
		Road max = getMaxPriority();
		counters.put(max, counters.get(max) + 1);
		priorities.put(max, 0);
		light = max;
		
	}

	public void changeLightDumb() {
		
		light = getDirection( light, Direction.RIGHT );
		
	}
	
	public void getCar(Road direction, Car car) {
		
		ArrayList<Car> carList = cars.get(direction);
		
		synchronized (carList) {
			carList.add(car);			
		}
		synchronized (this) {
			this.notifyAll();			
		}
	}
	
	public void letThrough(){
		
			
		ArrayList<Car> carList =  cars.get(light);
		
		synchronized (carList) {
			if(carList.size() > 0) {
				giveCar( cars.get(light).get(0) , light );
				cars.get(light).remove(0);			
			}
		}
		

	}
	
	public void carWait() {
		
		for (Entry<Road, ArrayList<Car>> entry : cars.entrySet()) {
			for(Car car : entry.getValue() ) {
				car.waitInLine();
			}
		}
		
	}
	
	public Road getDirection(Road road, Direction direction) {
		switch (road) {
		case NORTH:
			switch (direction) {
			case STRAIGHT:
				return Road.SOUTH;
			case RIGHT:
				return Road.WEST;
			case LEFT:
				return Road.EAST;
			}
		case SOUTH:
			switch (direction) {
			case STRAIGHT:
				return Road.NORTH;
			case RIGHT:
				return Road.EAST;
			case LEFT:
				return Road.WEST;
			}
		case EAST:
			switch (direction) {
			case STRAIGHT:
				return Road.WEST;
			case RIGHT:
				return Road.NORTH;
			case LEFT:
				return Road.SOUTH;
			}
		case WEST:
			switch (direction) {
			case STRAIGHT:
				return Road.EAST;
			case RIGHT:
				return Road.SOUTH;
			case LEFT:
				return Road.NORTH;
			}
		}
		return Road.SOUTH;
	}
	
	public void giveCar(Car car, Road direction) {

		car.move();
		Road r = getDirection(direction, car.turns.get(0));
		
		if( intersections.get(r) == null ) {
			saveTime(car);
		} else {
			intersections.get(r).getCar(getDirection(r, Direction.STRAIGHT), car);
		}
		
	}
	
	public void saveTime(Car car) {
		synchronized (waitingTime) {
			waitingTime.add( car.timeWaited );			
		}
	}
	
	public void quit() {
		Running = false;
	}
	
	@Override
	public void run() {
		
		while(Running){
		
			if(cars.get(Road.NORTH).size() > 0 || cars.get(Road.SOUTH).size() > 0 || cars.get(Road.WEST).size() > 0 || cars.get(Road.EAST).size() > 0) {
			
				System.out.println("Ez a " + id + ". kereszteződés.");
				System.out.println("Északról várnak: " + cars.get(Road.NORTH).size());
				System.out.println("Délről várnak: " + cars.get(Road.SOUTH).size());
				System.out.println("Keletről várnak: " + cars.get(Road.EAST).size());
				System.out.println("Nyugatról várnak: " + cars.get(Road.WEST).size());
				System.out.println();
				
				letThrough();
				carWait();
				counter++;
				if( counter % 20 == 0) {
					changeLight();
				}
				
				try {
					sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			} else {
				System.out.println("Ez a " + id + ". kereszteződés.");
				System.out.println("Üres minden");
				
				try {
					synchronized (this) {
						this.wait();						
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		timer.cancel();
		super.run();		
	}
}
